<template>
  <div class="bpmn-editor">
    <div class="toolbar">
      <button @click="addElement('task')">添加任务</button>
      <button @click="addElement('event')">添加事件</button>
      <button @click="addElement('gateway')">添加网关</button>
      <button @click="toggleLineMode">连线模式</button>
      <button @click="generateBpmn">生成 BPMN 文件</button>
    </div>

    <div class="canvas-container"
         @mousedown="onMouseDown"
         @mousemove="onMouseMove"
         @mouseup="onMouseUp"
         @mouseleave="onMouseLeave">
      <canvas ref="canvasRef" width="800" height="600"></canvas>
    </div>

    <div v-if="selectedElement" class="property-panel">
      <h3>属性编辑</h3>
      <div>
        <label for="name">名称：</label>
        <input v-model="selectedElement.name" id="name" />
      </div>
      <div>
        <label for="type">类型：</label>
        <input v-model="selectedElement.type" id="type" />
      </div>
    </div>
  </div>
</template>

<script setup>
import { ref, reactive, onMounted } from 'vue';

const canvasRef = ref(null);
const ctx = ref(null);
const elements = reactive([]);
const lines = reactive([]);
const selectedElement = ref(null);
const isDragging = ref(false);
const draggedElement = ref(null);
const isLineMode = ref(false);
const currentLine = reactive({ start: null, end: null });
const mousePosition = reactive({ x: 0, y: 0 });
const elementMargin = 20;
const draggedLine = ref(null); // 拖动的连接线
const isLineBeingDragged = ref(false); // 标记当前是否正在拖动连线
const dragStartPos = reactive({ x: 0, y: 0 }); // 记录拖动开始时的鼠标位置

// 初始化绘制上下文
onMounted(() => {
  if (canvasRef.value) {
    ctx.value = canvasRef.value.getContext('2d');
    drawElements();
  }
});

const addElement = (type) => {
  let xPos = 50;
  let yPos = 50;

  if (elements.length > 0) {
    const lastElement = elements[elements.length - 1];
    xPos = lastElement.x + lastElement.width + elementMargin;
    yPos = lastElement.y;
  }

  const newElement = {
    id: Date.now(),
    name: `${type} 元素`,
    type,
    x: xPos,
    y: yPos,
    width: 100,
    height: 50,
    color: type === 'task' ? 'blue' : type === 'event' ? 'green' : 'orange',
    borderStyle: 'solid',
    selected: false,
    icon: type === 'task' ? 'T' : type === 'event' ? 'E' : 'G',
  };

  elements.push(newElement);
  drawElements();
};

const toggleLineMode = () => {
  isLineMode.value = !isLineMode.value;
  if (!isLineMode.value) {
    currentLine.start = null;
    currentLine.end = null;
  }
};

const drawElements = () => {
  if (ctx.value) {
    ctx.value.clearRect(0, 0, canvasRef.value.width, canvasRef.value.height);

    lines.forEach((line) => {
      const source = elements.find((el) => el.id === line.source);
      const target = elements.find((el) => el.id === line.target);
      if (source && target) {
        // 绘制连接线 (贝塞尔曲线)
        ctx.value.beginPath();
        ctx.value.moveTo(source.x + source.width / 2, source.y + source.height / 2);
        const controlX = (source.x + target.x) / 2;
        const controlY = (source.y + target.y) / 2;
        ctx.value.bezierCurveTo(controlX, controlY, controlX, controlY, target.x + target.width / 2, target.y + target.height / 2);

        // 绘制箭头
        const angle = Math.atan2(target.y - source.y, target.x - source.x);
        const arrowLength = 10;
        const arrowWidth = 5;
        ctx.value.lineTo(
          target.x + target.width / 2 - arrowLength * Math.cos(angle - Math.PI / 6),
          target.y + target.height / 2 - arrowLength * Math.sin(angle - Math.PI / 6)
        );
        ctx.value.moveTo(target.x + target.width / 2, target.y + target.height / 2);
        ctx.value.lineTo(
          target.x + target.width / 2 - arrowLength * Math.cos(angle + Math.PI / 6),
          target.y + target.height / 2 - arrowLength * Math.sin(angle + Math.PI / 6)
        );
        ctx.value.stroke();
      }
    });

    elements.forEach((element) => {
      ctx.value.fillStyle = element.selected ? 'lightblue' : element.color;
      ctx.value.fillRect(element.x, element.y, element.width, element.height);

      ctx.value.lineWidth = 2;
      ctx.value.strokeStyle = 'black';
      ctx.value.setLineDash(element.selected ? [5, 5] : []);
      ctx.value.strokeRect(element.x, element.y, element.width, element.height);
      ctx.value.fillStyle = 'white';
      ctx.value.fillText(element.name, element.x + 10, element.y + 20);

      ctx.value.fillStyle = 'black';
      ctx.value.font = '16px Arial';
      ctx.value.fillText(element.icon, element.x + 5, element.y + 15);
    });

    // 绘制当前连线（当处于连线模式时）
    if (currentLine.start) {
      const startElement = elements.find((el) => el.id === currentLine.start);
      if (startElement) {
        ctx.value.beginPath();
        ctx.value.moveTo(startElement.x + startElement.width / 2, startElement.y + startElement.height / 2);
        ctx.value.lineTo(mousePosition.x, mousePosition.y);
        ctx.value.stroke();
      }
    }

    // 拖动中的连线
    if (isLineBeingDragged.value && draggedLine.value) {
      const startElement = elements.find((el) => el.id === draggedLine.value.source);
      const endElement = elements.find((el) => el.id === draggedLine.value.target);

      if (startElement && endElement) {
        ctx.value.beginPath();
        ctx.value.moveTo(startElement.x + startElement.width / 2, startElement.y + startElement.height / 2);
        const controlX = (startElement.x + endElement.x) / 2;
        const controlY = (startElement.y + endElement.y) / 2;
        ctx.value.bezierCurveTo(controlX, controlY, controlX, controlY, endElement.x + endElement.width / 2, endElement.y + endElement.height / 2);
        ctx.value.stroke();
      }
    }
  }
};

const onMouseDown = (event) => {
  const { offsetX: x, offsetY: y } = event;
  dragStartPos.x = x;
  dragStartPos.y = y;

  if (isLineMode.value) {
    const startElement = elements.find((element) => x > element.x && x < element.x + element.width && y > element.y && y < element.y + element.height);
    if (startElement) {
      currentLine.start = startElement.id;
    }
  } else {
    elements.forEach((element) => {
      if (x > element.x && x < element.x + element.width && y > element.y && y < element.y + element.height) {
        if (selectedElement.value !== element) {
          if (selectedElement.value) {
            selectedElement.value.selected = false;
          }
          selectedElement.value = element;
          element.selected = true;
        }

        // 标记当前元素为拖动状态
        isDragging.value = true;
        draggedElement.value = element;
      } else {
        element.selected = false;
      }
    });
  }

  // 处理连线拖动
  lines.forEach((line) => {
    const sourceElement = elements.find((el) => el.id === line.source);
    const targetElement = elements.find((el) => el.id === line.target);

    if (sourceElement && targetElement) {
      const startX = sourceElement.x + sourceElement.width / 2;
      const startY = sourceElement.y + sourceElement.height / 2;
      const endX = targetElement.x + targetElement.width / 2;
      const endY = targetElement.y + targetElement.height / 2;

      if (Math.abs(x - startX) < 10 && Math.abs(y - startY) < 10) {
        draggedLine.value = line;
        isLineBeingDragged.value = true;
      }
    }
  });

  drawElements();
};

const onMouseMove = (event) => {
  mousePosition.x = event.offsetX;
  mousePosition.y = event.offsetY;

  // 如果在拖动元素
  if (isDragging.value && draggedElement.value) {
    draggedElement.value.x = mousePosition.x - draggedElement.value.width / 2;
    draggedElement.value.y = mousePosition.y - draggedElement.value.height / 2;
    drawElements();
  } else {
    drawElements();
  }
};

const onMouseUp = () => {
  if (isLineMode.value && currentLine.start) {
    const endElement = elements.find((element) => mousePosition.x > element.x && mousePosition.x < element.x + element.width && mousePosition.y > element.y && mousePosition.y < element.y + element.height);
    if (endElement && currentLine.start !== endElement.id) {
      lines.push({ source: currentLine.start, target: endElement.id });
    }
    currentLine.start = null;
  }

  isLineBeingDragged.value = false;
  isDragging.value = false;
  draggedElement.value = null;
};

const onMouseLeave = () => {
  isLineBeingDragged.value = false;
};

const generateBpmn = () => {
  const bpmnXml = generateBpmnXml();
  downloadBpmnFile(bpmnXml);
};

const generateBpmnXml = () => {
  let bpmnXml = `
<definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL">
  <process id="process" name="流程" isExecutable="false">`;

  elements.forEach((element) => {
    if (element.type === 'task') {
      bpmnXml += `
    <task id="task_${element.id}" name="${element.name}" />`;
    } else if (element.type === 'event') {
      bpmnXml += `
    <startEvent id="event_${element.id}" name="${element.name}" />`;
    } else if (element.type === 'gateway') {
      bpmnXml += `
    <exclusiveGateway id="gateway_${element.id}" name="${element.name}" />`;
    }
  });

  lines.forEach((line) => {
    bpmnXml += `
    <sequenceFlow id="flow_${line.source}_${line.target}" sourceRef="task_${line.source}" targetRef="task_${line.target}" />`;
  });

  bpmnXml += `
  </process>
</definitions>`;

  return bpmnXml;
};

const downloadBpmnFile = (bpmnXml) => {
  const blob = new Blob([bpmnXml], { type: 'application/xml' });
  const url = URL.createObjectURL(blob);
  const a = document.createElement('a');
  a.href = url;
  a.download = 'process.bpmn';
  a.click();
  URL.revokeObjectURL(url);
};
</script>

<style scoped>
.bpmn-editor {
  display: flex;
}

.toolbar {
  width: 200px;
  padding: 20px;
  border-right: 1px solid #ccc;
}

.canvas-container {
  position: relative;
  flex-grow: 1;
  border: 1px solid #ccc;
}

.property-panel {
  position: absolute;
  right: 10px;
  top: 50px;
  width: 200px;
  padding: 10px;
  background-color: #fff;
  border: 1px solid #ccc;
}

canvas {
  border: 1px solid #000;
}

button {
  margin: 5px;
}

input {
  width: 100%;
  padding: 5px;
}
</style>
